{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Advanced RAG"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "s3_uri= \"s3://ashwin-partner-bucket/fashion_dataset.jsonl\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Sample Dataset with OpenAIEmbedding already included in the dataset"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>ageGroup</th>\n",
       "      <th>link</th>\n",
       "      <th>brandName</th>\n",
       "      <th>price</th>\n",
       "      <th>title</th>\n",
       "      <th>gender</th>\n",
       "      <th>subCategory</th>\n",
       "      <th>masterCategory</th>\n",
       "      <th>season</th>\n",
       "      <th>articleType</th>\n",
       "      <th>baseColour</th>\n",
       "      <th>id</th>\n",
       "      <th>openAIVec</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>Adults-Women</td>\n",
       "      <td>http://assets.myntassets.com/v1/images/style/p...</td>\n",
       "      <td>Inc 5</td>\n",
       "      <td>1390.0</td>\n",
       "      <td>Inc. 5 Women Casual White Flats</td>\n",
       "      <td>Women</td>\n",
       "      <td>Shoes</td>\n",
       "      <td>Footwear</td>\n",
       "      <td>Winter</td>\n",
       "      <td>Heels</td>\n",
       "      <td>White</td>\n",
       "      <td>22275</td>\n",
       "      <td>[-0.016259265699646003, -0.016057870922684, -0...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>Adults-Women</td>\n",
       "      <td>http://assets.myntassets.com/v1/images/style/p...</td>\n",
       "      <td>French Connection</td>\n",
       "      <td>3999.0</td>\n",
       "      <td>French Connection Women Black Sling Bag</td>\n",
       "      <td>Women</td>\n",
       "      <td>Bags</td>\n",
       "      <td>Accessories</td>\n",
       "      <td>Summer</td>\n",
       "      <td>Handbags</td>\n",
       "      <td>Black</td>\n",
       "      <td>42874</td>\n",
       "      <td>[-0.022201561751436002, 0.006381784873631001, ...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>Kids-Girls</td>\n",
       "      <td>http://assets.myntassets.com/v1/images/style/p...</td>\n",
       "      <td>Q&amp;Q</td>\n",
       "      <td>625.0</td>\n",
       "      <td>Q&amp;Q Kids Girls White Dial Analog Watch</td>\n",
       "      <td>Girls</td>\n",
       "      <td>Watches</td>\n",
       "      <td>Accessories</td>\n",
       "      <td>Winter</td>\n",
       "      <td>Watches</td>\n",
       "      <td>Pink</td>\n",
       "      <td>49888</td>\n",
       "      <td>[-0.001154974972115, 0.010144626266777, -7.218...</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "       ageGroup                                               link  \\\n",
       "0  Adults-Women  http://assets.myntassets.com/v1/images/style/p...   \n",
       "1  Adults-Women  http://assets.myntassets.com/v1/images/style/p...   \n",
       "2    Kids-Girls  http://assets.myntassets.com/v1/images/style/p...   \n",
       "\n",
       "           brandName   price                                    title gender  \\\n",
       "0              Inc 5  1390.0          Inc. 5 Women Casual White Flats  Women   \n",
       "1  French Connection  3999.0  French Connection Women Black Sling Bag  Women   \n",
       "2                Q&Q   625.0   Q&Q Kids Girls White Dial Analog Watch  Girls   \n",
       "\n",
       "  subCategory masterCategory  season articleType baseColour     id  \\\n",
       "0       Shoes       Footwear  Winter       Heels      White  22275   \n",
       "1        Bags    Accessories  Summer    Handbags      Black  42874   \n",
       "2     Watches    Accessories  Winter     Watches       Pink  49888   \n",
       "\n",
       "                                           openAIVec  \n",
       "0  [-0.016259265699646003, -0.016057870922684, -0...  \n",
       "1  [-0.022201561751436002, 0.006381784873631001, ...  \n",
       "2  [-0.001154974972115, 0.010144626266777, -7.218...  "
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import pandas as pd\n",
    "import s3fs\n",
    "import boto3\n",
    "from dotenv import load_dotenv\n",
    "\n",
    "load_dotenv()\n",
    "\n",
    "df = pd.read_json(s3_uri, orient=\"records\", lines=True)\n",
    "df[:3]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Insert Data to MongoDB Atlas"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from pymongo import MongoClient\n",
    "import certifi\n",
    "mongo_client = MongoClient(os.environ[\"MONGODB_CONNECTION_STR\"], tlsCAFile=certifi.where())\n",
    "# Upload documents along with vector embeddings to MongoDB Atlas Collection\n",
    "col = mongo_client[\"search\"][\"catalog_final_myn\"]\n",
    "col.insert_many(df.to_dict(orient=\"records\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "from langchain_core.output_parsers import JsonOutputParser # type: ignore\n",
    "from langchain_core.prompts import PromptTemplate # type: ignore\n",
    "from langchain_core.pydantic_v1 import BaseModel, Field # type: ignore\n",
    "from langchain_openai import ChatOpenAI # type: ignore\n",
    "\n",
    "from langchain_openai.embeddings import OpenAIEmbeddings # type: ignore\n",
    "from langchain_mongodb.vectorstores import MongoDBAtlasVectorSearch # type: ignore\n",
    "\n",
    "\n",
    "from pymongo import MongoClient # type: ignore\n",
    "from typing import List\n",
    "from itertools import chain\n",
    "import certifi # type: ignore\n",
    "import os\n",
    "from dotenv import load_dotenv # type: ignore\n",
    "\n",
    "load_dotenv()\n",
    "\n",
    "from functools import lru_cache\n",
    "\n",
    "@lru_cache\n",
    "def get_openai_emb_transformers():\n",
    "    \"\"\"\n",
    "    Returns an instance of OpenAIEmbeddings for OpenAI transformer models.\n",
    "    \n",
    "    This function creates and returns an instance of the OpenAIEmbeddings class,\n",
    "    which provides access to OpenAI transformer models for natural language processing.\n",
    "    The instance is cached using the lru_cache decorator for efficient reuse.\n",
    "    \n",
    "    Returns:\n",
    "        embeddings (OpenAIEmbeddings): An instance of the OpenAIEmbeddings class.\n",
    "    \"\"\"\n",
    "    embeddings = OpenAIEmbeddings()\n",
    "    return embeddings\n",
    "\n",
    "@lru_cache\n",
    "def get_vector_store():\n",
    "    \"\"\"\n",
    "    Retrieves the vector store for MongoDB Atlas.\n",
    "\n",
    "    Returns:\n",
    "        MongoDBAtlasVectorSearch: The vector store object.\n",
    "    \"\"\"\n",
    "    vs = MongoDBAtlasVectorSearch(collection=col, embedding=get_openai_emb_transformers(), index_name=\"vector_index_openAi_cosine\", embedding_key=\"openAIVec\", text_key=\"title\")\n",
    "    return vs\n",
    "\n",
    "@lru_cache(10)\n",
    "def get_conversation_chain_conv():\n",
    "    \"\"\"\n",
    "    Retrieves a conversation chain model for chat conversations.\n",
    "\n",
    "    Returns:\n",
    "        ChatOpenAI: The conversation chain model for chat conversations.\n",
    "    \"\"\"\n",
    "    llm = ChatOpenAI(model=\"gpt-3.5-turbo\", temperature=0.2, max_tokens=2048)\n",
    "    # chain = ConversationChain(llm=llm, memory=ConversationBufferWindowMemory(k=5))\n",
    "    return llm\n",
    "\n",
    "\n",
    "# Define your desired data structure.\n",
    "class ProductRecoStatus(BaseModel):\n",
    "    \"\"\"\n",
    "    Represents the status of product recommendations.\n",
    "\n",
    "    Attributes:\n",
    "        relevancy_status (bool): Product recommendation status conditioned on the context of the input query.\n",
    "                                 True if the query is related to purchasing fashion clothing and/or accessories.\n",
    "                                 False otherwise.\n",
    "        recommendations (List[str]): List of recommended product titles based on the input query context and\n",
    "                                     if the relevancy_status is True.\n",
    "    \"\"\"\n",
    "    relevancy_status: bool = Field(description=\"Product recommendation status is conditioned on the fact if the context of input query is to purchase a fashion clothing and or fashion accessories.\")\n",
    "    recommendations: List[str] = Field(description=\"list of recommended product titles based on the input query context and if recommendation_status is true.\")\n",
    "\n",
    "\n",
    "class Product(BaseModel):\n",
    "    \"\"\"\n",
    "    Represents a product.\n",
    "\n",
    "    Attributes:\n",
    "        title (str): Title of the product.\n",
    "        baseColour (List[str]): List of base colours of the product.\n",
    "        gender (List[str]): List of genders the product is targeted for.\n",
    "        articleType (str): Type of the article.\n",
    "        mfg_brand_name (str): Manufacturer or brand name of the product.\n",
    "    \"\"\"\n",
    "    title: str = Field(description=\"Title of the product.\")\n",
    "    baseColour: List[str] = Field(description=\"List of base colours of the product.\")\n",
    "    gender: List[str] = Field(description=\"List of genders the product is targeted for.\")\n",
    "    articleType: str = Field(description=\"Type of the article.\")\n",
    "    mfg_brand_name: str = Field(description=\"Manufacturer or brand name of the product.\")\n",
    "\n",
    "\n",
    "class Recommendations(BaseModel):\n",
    "    \"\"\"\n",
    "    Represents a set of recommendations for products and a message to the user.\n",
    "\n",
    "    Attributes:\n",
    "        products (List[Product]): List of recommended products.\n",
    "        message (str): Message to the user and context of the chat history summary.\n",
    "    \"\"\"\n",
    "    products: List[Product] = Field(description=\"List of recommended products.\")\n",
    "    message: str = Field(description=\"Message to the user and context of the chat history summary.\")\n",
    "\n",
    "\n",
    "reco_status_parser = JsonOutputParser(pydantic_object=ProductRecoStatus)\n",
    "\n",
    "reco_status_prompt = PromptTemplate(\n",
    "    template=\"You are AI assistant tasked at identifying if there is a product purchase intent in the query and providing suitable fashion recommendations.\\n{format_instructions}\\n{query}\\n\\\n",
    "        #Chat History Summary: {chat_history}\\n\\nBased on the context of the query, please provide the relevancy status and list of recommended products.\",\n",
    "    input_variables=[\"query\", \"chat_history\"],\n",
    "    partial_variables={\"format_instructions\": reco_status_parser.get_format_instructions()},\n",
    ")\n",
    "\n",
    "reco_parser = JsonOutputParser(pydantic_object=Recommendations)\n",
    "reco_prompt = PromptTemplate(\n",
    "    input_variables=[\"question\", \"recommendations\", \"chat_history\"],\n",
    "    partial_variables={\"format_instructions\": reco_parser.get_format_instructions()},\n",
    "    template=\"\\n User query:{question} \\n Chat Summary: {chat_history} \\n Rank and suggest me suitable products for creating grouped product recommendations given all product recommendations below feature atleast one product for each articleType \\n {recommendations} \\n show output in {format_instructions} for top 10 products\"\n",
    ")\n",
    "\n",
    "\n",
    "def get_product_reco_status(query: str, chat_history: List[str] = []):\n",
    "    \"\"\"\n",
    "    Retrieves the recommendation status for a product based on the given query and chat history.\n",
    "\n",
    "    Args:\n",
    "        query (str): The query to be used for retrieving the recommendation status.\n",
    "        chat_history (List[str]): The chat history containing previous conversations.\n",
    "\n",
    "    Returns:\n",
    "        The response containing the recommendation status.\n",
    "    \"\"\"\n",
    "    llm = get_conversation_chain_conv()\n",
    "    chain = reco_status_prompt | llm | reco_status_parser\n",
    "    resp = chain.invoke({\"query\": query, \"chat_history\": chat_history})\n",
    "    return resp\n",
    "\n",
    "def get_sorted_results(product_recommendations):\n",
    "    all_titles = [rec['title'] for rec in product_recommendations['products']]\n",
    "    results = list(col.find({\"title\": {\"$in\":all_titles}}, {\"_id\": 0 , \"id\":1, \"title\": 1, \"price\": 1, \"baseColour\": 1, \"articleType\": 1, \"gender\": 1, \"link\" : 1, \"mfg_brand_name\": 1}))\n",
    "    sorted_results = []\n",
    "    for title in all_titles:\n",
    "        for result in results:\n",
    "            if result['title'] == title:\n",
    "                sorted_results.append(result)\n",
    "                break\n",
    "    return sorted_results\n",
    "\n",
    "def get_product_recommendations(query: str, reco_queries: List[str], chat_history: List[str]=[]):\n",
    "    \"\"\"\n",
    "    Retrieves product recommendations based on the given query and chat history.\n",
    "\n",
    "    Args:\n",
    "        query (str): The query string for the recommendation.\n",
    "        chat_history (List[str]): The list of previous chat messages.\n",
    "        filter_query (dict): The filter query to apply during the recommendation retrieval.\n",
    "        reco_queries (List[str]): The list of recommendation queries.\n",
    "\n",
    "    Returns:\n",
    "        dict: The response containing the recommendations.\n",
    "\n",
    "    \"\"\"\n",
    "    vectorstore = get_vector_store()\n",
    "    retr = vectorstore.as_retriever(search_kwargs={\"k\": 10})\n",
    "    all_recommendations = list(chain(*retr.batch(reco_queries)))\n",
    "    llm = get_conversation_chain_conv()\n",
    "    llm_chain = reco_prompt | llm | reco_parser\n",
    "    resp = llm_chain.invoke({\"question\": query, \"chat_history\": chat_history, \"recommendations\": [v.page_content for v in all_recommendations]})\n",
    "    resp = get_sorted_results(resp)\n",
    "    return resp"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'relevancy_status': True, 'recommendations': ['Floral Wrap Dress', 'Off-Shoulder Maxi Dress', 'Lace Fit and Flare Dress', 'Ruffled Hem Shift Dress', 'Denim Shirt Dress']}\n",
      "[{'link': 'http://assets.myntassets.com/v1/images/style/properties/ebb8a69f6e56cf47f9fefd3ac23cfe03_images.jpg', 'price': 690.0, 'title': 'Femella Women Floral Red Dress', 'gender': 'Women', 'mfg_brand_name': 'Femella', 'articleType': 'Dresses', 'baseColour': 'Red', 'id': '39217'}, {'link': 'http://assets.myntassets.com/v1/images/style/properties/730a44ed829f9310beb97f583a960262_images.jpg', 'price': 3800.0, 'title': 'Forever New Women Floral Purple Dress', 'gender': 'Women', 'mfg_brand_name': 'Forever New', 'articleType': 'Dresses', 'baseColour': 'Purple', 'id': '8484'}, {'link': 'http://assets.myntassets.com/v1/images/style/properties/4e056a5231b2f0f427e2b9f2d45a2662_images.jpg', 'price': 1849.0, 'title': 'Mineral Women Floral Orange Dress', 'gender': 'Women', 'mfg_brand_name': 'Mineral', 'articleType': 'Dresses', 'baseColour': 'Orange', 'id': '37912'}, {'link': 'http://assets.myntassets.com/v1/images/style/properties/498885fabd8f2adb98e3563814a7be15_images.jpg', 'price': 1999.0, 'title': 'Arrow Woman Multi Coloured Floral Dress', 'gender': 'Women', 'mfg_brand_name': 'Arrow Woman', 'articleType': 'Dresses', 'baseColour': 'Multi', 'id': '23050'}, {'link': 'http://assets.myntassets.com/v1/images/style/properties/Tonga-White-Floral-Design-Dress_c8060cca2e01d587c96badd11844ecdd_images.jpg', 'price': 2190.0, 'title': 'Tonga White Floral Design Dress', 'gender': 'Women', 'mfg_brand_name': 'Tonga', 'articleType': 'Dresses', 'baseColour': 'White', 'id': '58499'}, {'link': 'http://assets.myntassets.com/v1/images/style/properties/a4be477154c1abe180dd8875e082ad6d_images.jpg', 'price': 599.0, 'title': 'Doodle Kids Girls Navy Blue Floral Print Dress', 'gender': 'Girls', 'mfg_brand_name': 'Doodle', 'articleType': 'Dresses', 'baseColour': 'Navy Blue', 'id': '23621'}, {'link': 'http://assets.myntassets.com/v1/images/style/properties/33abe02e37b16010b068ebc37de2b42a_images.jpg', 'price': 4000.0, 'title': 'Forever New Women Blossom Silk Cream Dress', 'gender': 'Women', 'mfg_brand_name': 'Forever New', 'articleType': 'Dresses', 'baseColour': 'Cream', 'id': '8479'}, {'link': 'http://assets.myntassets.com/v1/images/style/properties/555c6808b415c53b60a6c24cbc7b4274_images.jpg', 'price': 4200.0, 'title': 'Forever New Women GeoPrint Multi Coloured Dress', 'gender': 'Women', 'mfg_brand_name': 'Forever New', 'articleType': 'Dresses', 'baseColour': 'Multi', 'id': '8500'}, {'link': 'http://assets.myntassets.com/v1/images/style/properties/Femella-Women-Multi-Coloured-Dress_f866e35c53c70998b24e0315dfad8051_images.jpg', 'price': 890.0, 'title': 'Femella Women Multi Coloured Dress', 'gender': 'Women', 'mfg_brand_name': 'Femella', 'articleType': 'Dresses', 'baseColour': 'Multi', 'id': '59896'}, {'link': 'http://assets.myntassets.com/v1/images/style/properties/c1283d633e7469b32f8f16066fb511de_images.jpg', 'price': 769.0, 'title': 'Span Women Floral Printed Green Kurta', 'gender': 'Women', 'mfg_brand_name': 'Span', 'articleType': 'Kurtas', 'baseColour': 'Green', 'id': '40608'}]\n",
      "1. A flowy floral maxi dress\n",
      "2. A cute off-the-shoulder sundress\n",
      "3. A simple wrap dress in a solid color\n",
      "4. A striped shirt dress with a belt\n",
      "5. A denim overall dress\n",
      "6. A ruffled mini dress\n",
      "7. A boho-inspired peasant dress\n",
      "8. A lace-trimmed slip dress\n",
      "9. A polka dot fit and flare dress\n",
      "10. A casual jumpsuit with a cinched waist.\n"
     ]
    }
   ],
   "source": [
    "query = \"Can you suggest me some Casual dresses for date occassion with my boy friend\"\n",
    "status = get_product_reco_status(query)\n",
    "print(status)\n",
    "print(get_product_recommendations(query, reco_queries=status[\"recommendations\"], chat_history=[]))\n",
    "print(get_conversation_chain_conv().invoke(query).content)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'relevancy_status': False, 'recommendations': []}\n",
      "There are many great options for a date with your boyfriend, depending on your interests and preferences. Some ideas could include:\n",
      "\n",
      "1. A romantic dinner at a nice restaurant\n",
      "2. A picnic in the park or at the beach\n",
      "3. A movie night at home with homemade popcorn and snacks\n",
      "4. A hike or nature walk followed by a picnic\n",
      "5. A visit to a local museum or art gallery\n",
      "6. A cooking class or wine tasting experience\n",
      "7. A day trip to a nearby city or town to explore and try new restaurants\n",
      "8. A couples massage or spa day\n",
      "9. A fun activity like mini golf, bowling, or go-kart racing\n",
      "10. A concert or live music event\n",
      "\n",
      "Ultimately, the best date idea is one that you both will enjoy and that allows you to spend quality time together. Consider your boyfriend's interests and preferences when planning the date to ensure it is a memorable and enjoyable experience for both of you.\n"
     ]
    }
   ],
   "source": [
    "query = \"Where should I take my boy friend for date\" \n",
    "status = get_product_reco_status(query) \n",
    "print(status) \n",
    "print(get_conversation_chain_conv().invoke(query).content) "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "There are many great options for a date with your boyfriend, depending on your interests and preferences. Some ideas could include: \n",
    " \n",
    "1. A romantic dinner at a nice restaurant \n",
    "2. A picnic in the park or at the beach \n",
    "3. A movie night at home or at the cinema \n",
    "4. A hike or nature walk \n",
    "5. A visit to a museum or art gallery \n",
    "6. A cooking class or wine tasting \n",
    "7. A concert or live music event \n",
    "8. A day trip to a nearby city or town \n",
    "9. A couples massage or spa day \n",
    "10. A fun activity like mini golf, bowling, or go-kart racing \n",
    " \n",
    "Ultimately, the best date idea is one that you both will enjoy and that allows you to spend quality time together. Consider what you both like to do and choose an activity that will create lasting memories. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "language_info": {
   "name": "python"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
